package com.cxx.factory;

import com.cxx.annotation.Autowired;
import com.cxx.annotation.Service;
import com.cxx.annotation.Transactional;
import com.cxx.utils.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.reflections.Reflections;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * java Bean 创建工厂
 *
 * @author Shuhang He
 * @version 1.0
 * @date 2021/3/14 20:01
 */
public class BeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */
    private static Map<String, Object> map = new HashMap<>();

    static {
        // 获取xml的字节流
        InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("applicationContext.xml");
        //通过dom4j解析xml
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(inputStream);
            Element rootElement = document.getRootElement();
            // 初始化bean
            doInstanceBean(rootElement);

            // 处理bean之间的依赖关系
            doAutowired(rootElement);

            // 处理事务
            doTransactional();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取实例化后的对象
     * @param id
     * @return
     */
    public static Object getBean(String id) {
        return map.get(id);
    }


    /**
     * 初始化bean
     */
    private static void doInstanceBean(Element rootElement) throws Exception{
        // 此处是为了获取在xml中配置的bean
        //遍历bean标签，获取其中的属性
        List<Element> beans = rootElement.selectNodes("//bean");
        for (Element selectNode : beans) {
            // 获取javaBean的ID
            String id = selectNode.attributeValue("id");
            // 获取javaBean的全限定类名
            String clazz = selectNode.attributeValue("class");
            // 通过反射技术实例化类
            Class<?> beanClass = Class.forName(clazz);
            Object bean = beanClass.getDeclaredConstructor().newInstance();
            // 将实例化后的对象存入map集合中
            map.put(id, bean);
        }

        // 这里是为了获取通过@Service注解来交给IoC托管的bean
        // 获取配置文件中的component-scan标签，该标签作用是为了获取需要扫描的包名
        Element scanElement = (Element) rootElement.selectSingleNode("//component-scan");
        // 获取需要扫描的包名
        String packageName = scanElement.attributeValue("base-package");
        Reflections reflections = new Reflections(packageName);
        // 获取包下所有拥有@Service注解的类
        Set<Class<?>> beanSet = reflections.getTypesAnnotatedWith(Service.class);
        for (Class<?> beanClass : beanSet) {
            Object o = beanClass.getDeclaredConstructor().newInstance();
            // 获取注解，判断value的值是否已经设置，如果value的值已经设置，则key取value的值
            Service annotation = beanClass.getAnnotation(Service.class);
            String annotationName = annotation.value();
            if (!StringUtils.isEmpty(annotationName)) {
                map.put(annotationName, o);
            } else {
                // 如果注解没有设置，则以接口的类名首字母小写作为key
                Class<?>[] interfaces = beanClass.getInterfaces();
                if (interfaces.length > 0) {
                    for (Class<?> anInterface : interfaces) {
                        String simpleName = anInterface.getSimpleName();
                        map.put(StringUtils.lowerCaseName(simpleName), o);
                    }
                } else {
                    map.put(StringUtils.lowerCaseName(beanClass.getSimpleName()), o);
                }
            }

        }

    }

    /**
     * 处理bean之间的依赖关系
     * @param rootElement
     * @throws Exception
     */
    public static void doAutowired(Element rootElement) throws Exception {
        // 此处是实现通过xml配置依赖关系时候的注入
        List<Element> propertyList = rootElement.selectNodes("//property");
        for (Element element : propertyList) {
            // 获取需要引用的实例化对象的id
            String name = element.attributeValue("name");
            //获取父元素
            String parentId = element.getParent().attributeValue("id");
            Object parentObject = map.get(parentId);
            // 遍历父对象中的所有方法，找出需要注入到类方法
            for (Method method : parentObject.getClass().getMethods()) {
                if (method.getName().equals("set" + StringUtils.capitalName(name))) {
                    method.invoke(parentObject, map.get(name));
                    break;
                }
            }
        }
        // 通过遍历所有的已经存在map中的bean，看是否有字段使用了@Autowired
        for (Map.Entry<String, Object> beanEntry : map.entrySet()) {
            Object value = beanEntry.getValue();
            Class<?> aClass = value.getClass();
            // 获取所有的字段值
            Field[] fields = aClass.getDeclaredFields();
            // 遍历所有字段值，判断是否有@Autowired注解,完成属性的注入
            for (Field field : fields) {
                // 如果该注解存在，说明需要属性注入
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired annotation = field.getAnnotation(Autowired.class);
                    String keyName = annotation.value();

                    String key = StringUtils.lowerCaseName(field.getType().getSimpleName());
                    if (!StringUtils.isEmpty(keyName)) {
                        key = keyName;
                    }
                    // 设置该属性可访问
                    field.setAccessible(true);
                    field.set(value, map.get(key));
                }
            }
        }
    }

    // 声明式事务处理
    private static void doTransactional() {
        for (Map.Entry<String, Object> objectEntry : map.entrySet()) {
            Object value = objectEntry.getValue();
            Class<?> aClass = value.getClass();
            // 下边的过程是完成声明式事务
            Method[] methods = aClass.getMethods();
            boolean methodTransactional = false;
            List<String> methodStr = new ArrayList<>();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Transactional.class)) {
                    methodTransactional = true;
                    methodStr.add(method.getName());
                }
            }
            ProxyFactory proxyFactory = (ProxyFactory) map.get("proxyFactory");
            Class<?>[] interfaces = aClass.getInterfaces();
            // 如果类上，有@Transactional注解，说明本类的所有方法都需要事务控制
            if (aClass.isAnnotationPresent(Transactional.class)) {

                // 如果没有实现接口，则采用cglib代理
                if (interfaces.length == 0) {
                    value = proxyFactory.getCglibProxy(value);
                } else {
                    value = proxyFactory.getJDKProxy(value);
                }
            } else if (methodTransactional){
                // 如果没有实现接口，则采用cglib代理
                if (interfaces.length == 0) {
                    value = proxyFactory.getSomeMethodCglibProxy(value, methodStr);
                } else {
                    value = proxyFactory.getSomeMethodJDKProxy(value, methodStr);
                }
            }
            map.put(objectEntry.getKey(), value);
        }

    }


}
